package com.awesome.common.util.file;

import android.text.TextUtils;

import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * Author: JFangZ
 * Email: janezhang1901@163.com
 * Date: 2020/11/16 10:53
 * Description:文件相关工具类
 */
public class FileUtil {
    /**
     * Author: JFangZ
     * Email: janezhang1901@163.com
     * Date:2020/11/16 11:01
     * Description:读取文件
     *
     * @param path 目标路径
     * @return 读取到的二进制数组
     */
    public static byte[] readFile(String path) {
        ByteArrayOutputStream byteArrayOutputStream = null;
        FileInputStream fileInputStream = null;

        try {
            File file = new File(path);
            fileInputStream = new FileInputStream(file);
            byteArrayOutputStream = new ByteArrayOutputStream(fileInputStream.available());
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = fileInputStream.read(bytes)) != -1) {
                byteArrayOutputStream.write(bytes, 0, len);
            }
            byteArrayOutputStream.flush();
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (byteArrayOutputStream != null) {
                try {
                    byteArrayOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * Author: JFangZ
     * Email: janezhang1901@163.com
     * Date:2020/11/16 11:08
     * Description:写入文件
     *
     * @param path 目标路径
     * @return true 表示写入完成，false 表示写入过程中出现异常
     */
    public static boolean writeFile(byte[] bytes, String path) {
        FileOutputStream fileOutputStream = null;
        try {
            File file = new File(path);
            if (file.getParentFile() != null) {
                file.getParentFile().mkdirs();
            }
            file.createNewFile();
            fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write(bytes);
            fileOutputStream.flush();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date: 2021/7/22 9:13
     * Description: 复制文件
     */
    public static boolean copy(File inFile, File outFile) {
        if (!inFile.exists()) return false;
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(inFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        }
        return copy(inputStream, outFile);
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date: 2021/7/22 9:13
     * Description: 复制文件
     */
    public static boolean copy(InputStream inputStream, File outFile) {
        if (!outFile.exists()) {
            File fileParentDir = outFile.getParentFile();
            if (!fileParentDir.exists()) {
                if (!fileParentDir.mkdirs()) return false;
            }
            try {
                if (!outFile.createNewFile()) return false;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(outFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        }
        return copy(inputStream, outputStream);
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date: 2021/7/22 9:13
     * Description: 复制文件
     */
    public static boolean copy(File inFile, OutputStream outputStream) {
        if (!inFile.exists()) return false;
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(inFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        }
        return copy(inputStream, outputStream);
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date: 2021/7/22 9:13
     * Description: 复制文件
     */
    public static boolean copy(InputStream inputStream, OutputStream outputStream) {
        try {
            byte[] buffer = new byte[1024];
            int realLength = 0;
            while ((realLength = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, realLength);
            }
            close(inputStream);
            close(outputStream);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date: 2021/7/22 9:13
     * Description: 关闭流
     */
    public static void close(Closeable... closeables) {
        if (closeables == null) return;
        for (Closeable closeable: closeables) {
            if (closeable != null) {
                try {
                    closeable.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date: 2021/7/22 9:13
     * Description: 删除文件
     */
    public static boolean delete(String path) {
        if (TextUtils.isEmpty(path)) return false;
        File file = new File(path);
        return delete(file);
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date: 2021/7/22 9:13
     * Description: 删除文件
     */
    public static boolean delete(File file) {
        if (file == null) return false;
        return file.exists() && file.delete();
    }

    /**
     * Author: JFangZ
     * Email: janezhang1901@163.com
     * Date:2020/11/16 11:11
     * Description:获取文件大小
     *
     * @param file 目标文件
     * @return 文件大小
     */
    public static long getFileSize(File file) {
        return file.length();
    }

    /**
     * Author: JFangZ
     * Email: janezhang1901@163.com
     * Date:2020/11/16 11:20
     * Description:获取文件夹大小
     *
     * @param file 目标文件夹
     * @return 文件夹大小
     */
    public static long getDirSize(File file) {
        File[] files = file.listFiles();
        if (files == null) {
            return 0;
        }
        long size = 0;
        for (File f : files) {
            if (f.isDirectory()) {
                // 文件夹默认也有大小,但不计入大小中,直接递归遍历子文件夹
                size += getDirSize(new File(f.getAbsolutePath()));
            } else {
                size += f.length();
            }
        }
        return size;
    }

    /**
     * Author: JFangZ
     * Email: janezhang1901@163.com
     * Date:2020/11/16 11:22
     * Description:获取目标文件夹下包含的文件个数
     *
     * @param file 目标文件
     * @return 文件个数
     */
    public static int getFileCount(File file) {
        File[] files = file.listFiles();
        if (files == null) {
            return 0;
        }
        int count = files.length;
        for (File f : files) {
            if (f.isDirectory()) {
                // 是文件夹,递归遍历子文件夹
                count += getFileCount(new File(f.getAbsolutePath()));
                // 文件夹不计入文件数,所以自减 1
                count--;
            }
        }
        return count;
    }

    /**
     * Author: JFangZ
     * Email: janezhang1901@163.com
     * Date:2020/11/16 11:25
     * Description:获取目标文件夹下包含的文件夹个数
     *
     * @param file 目标文件夹
     * @return 文件夹个数
     */
    public static int getDirCount(File file) {
        File[] files = file.listFiles();
        if (files == null) {
            return 0;
        }
        int count = 0;
        for (File f : files) {
            if (f.isDirectory()) {
                count++;
            }
        }
        return count;
    }

    /**
     * Author: JFangZ
     * Email: janezhang1901@163.com
     * Date:2020/11/16 14:17
     * Description:根据关键字获取文件列表,重载 {@link #getFileList(File, String, boolean)}
     */
    public static List<File> getFileList(File file, String keyword) {
        return getFileList(file, keyword, true);
    }

    /**
     * Author: JFangZ
     * Email: janezhang1901@163.com
     * Date:2020/11/16 14:17
     * Description:根据关键字获取文件列表
     *
     * @param file       需要检索的文件夹
     * @param keyword    关键字
     * @param ignoreCase 忽略大小写
     * @return 搜索结果
     */
    public static List<File> getFileList(File file, String keyword, boolean ignoreCase) {
        File[] files = file.listFiles();
        if (files == null) {
            return new ArrayList<>();
        }
        List<File> fileList = new ArrayList<>();
        for (File f : files) {
            if (f.isDirectory()) {
                // 是文件夹则继续递归搜索
                fileList.addAll(getFileList(new File(f.getAbsolutePath()), keyword));
            } else {
                // 是文件的话判断文件名
                if (ignoreCase) {
                    // 忽略大小写
                    if (f.getName().toLowerCase().contains(keyword.toLowerCase())) {
                        fileList.add(f);
                    }
                } else {
                    // 大小写精准匹配
                    if (f.getName().contains(keyword)) {
                        fileList.add(f);
                    }
                }
            }
        }
        return fileList;
    }

    /**
     * Author: JFangZ
     * Email: janezhang1901@163.com
     * Date:2020/11/16 14:11
     * Description:获取文件类型
     */
    public static String getFileType(String path) {
        String typeSeparator = ".";
        // 以最后一个 "." 后的字符串作为文件类型
        int typeSeparatorIndex = path.lastIndexOf(typeSeparator);
        if (typeSeparatorIndex == -1) {
            return null;
        }
        String paramSeparator = "?";
        // 以最后一个 "." 后到第一个 "?" 之间的字符串作为文件类型
        int paramSeparatorIndex = path.indexOf(paramSeparator);
        if (paramSeparatorIndex == -1) {
            return path.substring(typeSeparatorIndex + typeSeparator.length());
        } else {
            return path.substring(typeSeparatorIndex + typeSeparator.length(), paramSeparatorIndex);
        }
    }

    /**
     * Author: JFangZ
     * Email: janezhang1901@163.com
     * Date:2020/8/3 14:14
     * Description:格式化文件大小,重载 {@link #formatSize(long, String)}
     */
    public static String formatSize(long size) {
        return formatSize(size, "");
    }

    /**
     * Author: JFangZ
     * Email: janezhang1901@163.com
     * Date:2020/8/3 14:14
     * Description:格式化文件大小
     *
     * @param size             文件大小
     * @param contactCharacter 最后格式化得到的大小和单位之间的连接字符
     * @return 格式化后的字符串, 根据单位四舍五入保留两位小数, 末尾为 0 会去掉 0
     */
    public static String formatSize(long size, String contactCharacter) {
        String unit;
        float result;
        if (size < 1024) {
            // 小于 1KB,单位为 B
            unit = "B";
            result = size;
        } else if (size < (1024 * 1024)) {
            // 小于 1MB,单位为 KB
            unit = "KB";
            result = size / (float) 1024;
        } else if (size < (1024 * 1024 * 1024)) {
            // 小于 1GB,单位为 MB
            unit = "MB";
            result = size / (float) (1024 * 1024);
        } else {
            unit = "GB";
            result = size / (float) (1024 * 1024 * 1024);
        }
        // 四舍五入保留两位小数
        result = new BigDecimal(result).setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
        if (result == (int) result) {
            // 小数部分为 0,不要后面两个 0
            return (int) result + contactCharacter + unit;
        } else if (result * 10 == (int) (result * 10)) {
            // 小数部分为 .x0,不要后一个 0
            return (int) (result * 10) / (float) 10 + contactCharacter + unit;
        } else {
            // 正常返回两位小数
            return result + contactCharacter + unit;
        }
    }
}
